var CodeMirror = function (){
    var tabSize = 4;

	function Stream(str) {
		// Don't rename these or CodeMirror's "perl" mode will break.
		this.string = str;
		this.pos = 0;
		this.tokenStart = 0;
	}
	Stream.prototype = {
        eol: function() { return this.pos >= this.string.length; },
        sol: function() { return this.pos === 0; },
        peek: function() { return this.string[this.pos]; },
		next: function() { return this.string[this.pos++]; },
        eat: function(match) {
			var c = this.string[this.pos];
			var isMatch = (typeof c === "string") &&
                (c === match || (match.test && match.test(c)) ||
                (typeof match === "function" && match(c)));
			return isMatch ? this.string[this.pos++] : undefined;
		},
        eatWhile: function(match) {
			var ate = false;
			while (this.eat(match) !== undefined) {
				ate = true;
			}
			return ate;
		},
        eatSpace: function() { return this.eatWhile(/\s/); },
		skipToEnd: function() { this.pos = this.string.length; },
		skipTo: function(ch) {
			var idx = this.string.indexOf(ch, this.pos);
			if (idx !== -1) {
				this.pos = idx;
				return true;
			}
			return false;
		},
		match: function(pattern, consume, caseFold) {
			consume = (consume === true || typeof consume === "undefined");
			if (typeof pattern === "string") {
				var str = caseFold ? this.string.toLowerCase() : this.string;
				pattern = caseFold ? pattern.toLowerCase() : pattern;
				var index = str.indexOf(pattern, this.pos);
				if (index !== -1 && consume) {
					this.pos = index + pattern.length;
				}
				return index !== -1;
			} else {
				var match = this.string.substring(this.pos).match(pattern);
				if (match && consume && typeof match[0] === "string") {
					this.pos += match.index + match[0].length;
				}
				return match;
			}
		},
		backUp: function(n) { this.pos -= n; },
        column: function() {
			var col = 0, i = 0;
			while (i < this.tokenStart) {
				col += (this.string[i++] === "\t") ? tabSize : 1;
			}
			return col;
		},
        indentation: function() {
			var index = this.string.search(/\S/);
			var col = 0, i = 0;
			while(i < index) {
				col += (this.string[i++] === "\t") ? tabSize : 1;
			}
			return col;
		},
        current: function() { return this.string.substring(this.tokenStart, this.pos); },
		advance: function() { this.tokenStart = this.pos; }
	};

    function Mirror(options) {
		this._modes = {};
		// This internal variable must be named "mimeModes" otherwise CodeMirror's "less" mode will fail.
		this.mimeModes = {};
		this.options = {};
		// Expose Stream as a property named "StringStream". This is required to support CodeMirror's Perl mode,
		// which monkey-patches CodeMirror.StringStream.prototype and will fail if that object doesn't exist.
		this.StringStream = Stream;
	}
	function keys(obj) {
		var k = [];
		for (var p in obj) {
			if (Object.prototype.hasOwnProperty.call(obj, p)) {
				k.push(p);
			}
		}
		return k;
	}
	Mirror.prototype = {
		options: {},
		setOption: function(option, value) { this.options[option] = value; },
		getOption: function(option) { return this.options[option]; },
		copyState: function(mode, state) {
			if (typeof mode.copyState === "function") { return mode.copyState(state); }
			var newState = {};
			for (var prop in state) {
				var value = state[prop];
				newState[prop] = (value instanceof Array) ? value.slice() : value;
			}
			return newState;
		},
        defineMode: function(name, modeFactory) {
			this._modes[name] = modeFactory;
		},
        defineMIME: function(mime, modeSpec) {
			this.mimeModes[mime] = modeSpec;
		},
        getMode: function(options, modeSpec) {
			var config = {}, modeFactory;
			if (typeof modeSpec === "string" && this.mimeModes[modeSpec]) {
				modeSpec = this.mimeModes[modeSpec];
			}
			if (typeof modeSpec === "object") {
				config = modeSpec;
				modeFactory = this._modes[modeSpec.name];
			}
			modeFactory = modeFactory || this._modes[modeSpec];
			if (typeof modeFactory !== "function") {
				throw "Mode not found " + modeSpec;
			}
			return modeFactory(options, config);
		},
        listModes: function() {
			return keys(this._modes);
		},
        listMIMEs: function() {
			return keys(this.mimeModes);
		},
		_getModeName: function(mime) {
			var mname = this.mimeModes[mime];
			if (typeof mname === "object") { mname = mname.name; }
			return mname;
		}
	};
    return new Mirror({});
}();
